
#include <BFont>
#include <BKeyEvent>
#include <BPainter>
#include <BButton>
#include <BSpring>
#include <BSystem>

#include <BMessageBox>
#include "member_BMessageBox.h"
#include "member_BStyle.h"
#include "IGlobal.h"

using namespace BWE;

#define member					(*(member_BMessageBox*)_ptr)
#define member_allocate()		_ptr = new member_BMessageBox(this)
#define member_release()		delete (member_BMessageBox*)_ptr

BMessageBox* GetMessageBox()
{
	static BHolder<BMessageBox> messageBox = new BMessageBox();
	messageBox->setModal(true);
	return messageBox;
}

Button BMessageBox::Information(BWidget* parent, const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Information));
	mbox->setParent(parent);
	return mbox->information(title, text, buttons, defaultButton);
}
Button BMessageBox::Warning(BWidget* parent, const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Warning));
	mbox->setParent(parent);
	return mbox->warning(title, text, buttons, defaultButton);
}
Button BMessageBox::Question(BWidget* parent, const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Question));
	mbox->setParent(parent);
	return mbox->question(title, text, buttons, defaultButton);
}
Button BMessageBox::Critical(BWidget* parent, const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Critical));
	mbox->setParent(parent);
	return mbox->critical(title, text, buttons, defaultButton);
}
Button BMessageBox::About(BWidget* parent, const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_About));
	mbox->setParent(parent);
	return mbox->about(title, text, buttons, defaultButton);
}

Button BMessageBox::Information(BWidget* parent, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Information));
	mbox->setTitle("Information");
	mbox->setParent(parent);
	return mbox->information(text, buttons, defaultButton);
}
Button BMessageBox::Warning(BWidget* parent, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Warning));
	mbox->setTitle("Warning");
	mbox->setParent(parent);
	return mbox->warning(text, buttons, defaultButton);
}
Button BMessageBox::Question(BWidget* parent, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Question));
	mbox->setTitle("Question");
	mbox->setParent(parent);
	return mbox->question(text, buttons, defaultButton);
}
Button BMessageBox::Critical(BWidget* parent, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_Critical));
	mbox->setTitle("Critical");
	mbox->setParent(parent);
	return mbox->critical(text, buttons, defaultButton);
}
Button BMessageBox::About(BWidget* parent, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setIcon(BSystem::TypedIcon(Icon_About));
	mbox->setTitle("About");
	mbox->setParent(parent);
	return mbox->about(text, buttons, defaultButton);
}

Button BMessageBox::Information(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Information));
	return mbox->information(title, text, buttons, defaultButton);
}
Button BMessageBox::Warning(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Warning));
	return mbox->warning(title, text, buttons, defaultButton);
}
Button BMessageBox::Question(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Question));
	return mbox->question(title, text, buttons, defaultButton);
}
Button BMessageBox::Critical(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Critical));
	return mbox->critical(title, text, buttons, defaultButton);
}
Button BMessageBox::About(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_About));
	return mbox->about(title, text, buttons, defaultButton);
}

Button BMessageBox::Information(const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setTitle("Information");
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Information));
	return mbox->information(text, buttons, defaultButton);
}
Button BMessageBox::Warning(const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setTitle("Warning");
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Warning));
	return mbox->warning(text, buttons, defaultButton);
}
Button BMessageBox::Question(const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setTitle("Question");
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Question));
	return mbox->question(text, buttons, defaultButton);
}
Button BMessageBox::Critical(const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setTitle("Critical");
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_Critical));
	return mbox->critical(text, buttons, defaultButton);
}
Button BMessageBox::About(const BString& text, int buttons, Button defaultButton)
{
	BMessageBox* mbox = GetMessageBox();
	mbox->setTitle("About");
	mbox->setParent(BApplication::activeWindow());
	mbox->setIcon(BSystem::TypedIcon(Icon_About));
	return mbox->about(text, buttons, defaultButton);
}

BMessageBox::BMessageBox(BWidget* parent) : BDialog(parent)
{
	member_allocate();
	member.buttonLayout = new BHBoxLayout("bottom");
	member.buttonLayout->addSpring(10);
	this->setLayout(member.buttonLayout, Part_Bottom);
}
BMessageBox::~BMessageBox()
{
	member_release();
}

bool BMessageBox::setLineSpacing(int lineSpacing)
{
	if (member.block.lineSpacing != lineSpacing)
	{
		member.block.lineSpacing = lineSpacing;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
int BMessageBox::lineSpacing() const
{
	return member.block.lineSpacing;
}

void BMessageBox::setLineFeed(bool lineFeed)
{
	if (member.lineFeed != lineFeed)
	{
		member.lineFeed = lineFeed;
		member.block.dirty = true;
		member.dirty = true;
		this->fresh();
	}
}
bool BMessageBox::lineFeed() const
{
	return member.lineFeed;
}

void BMessageBox::setWordWrap(bool wordWrap)
{
	if (member.block.wordWrap != wordWrap)
	{
		member.block.wordWrap = wordWrap;
		member.block.dirty = true;
		member.dirty = true;
		this->fresh();
	}
}
bool BMessageBox::wordWrap() const
{
	return member.block.wordWrap;
}

void BMessageBox::setTextAlign(Align textAlign)
{
	if (member.textAlign != textAlign)
	{
		member.textAlign = textAlign;
		member.dirty = true;
		this->fresh();
	}
}
Align BMessageBox::textAlign() const
{
	return member.textAlign;
}

void BMessageBox::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.dirty = true;
		if (this->checks(Signal_TextChanged))
			this->emit(Signal_TextChanged, member.text);
		this->fresh();
	}
}
void BMessageBox::addText(const BString& text)
{
	if (text.size())
	{
		member.text.append(text);
		member.dirty = true;
		if (this->checks(Signal_TextChanged))
			this->emit(Signal_TextChanged, member.text);
		this->fresh();
	}
}
const BString& BMessageBox::text() const
{
	return member.text;
}

void BMessageBox::setTextFormat(BTextFormat* textFormat)
{
	if (member.block.format() != textFormat)
	{
		member.block.setFormat(textFormat);
		member.block.dirty = true;
		member.dirty = true;
		this->emit(Signal_FormatChanged, textFormat);
		this->fresh();
	}
}
BTextFormat* BMessageBox::textFormat()
{
	return member.block.format();
}
const BTextFormat* BMessageBox::textFormat() const
{
	return member.block.format();
}

int BMessageBox::setButtons(int buttons)
{
	clearButtons();
	int b = 1;
	for (int i = 0; i < 32; i++)
	{
		b <<= 1;
		if (buttons & b)
		{
			appendButton((Button)b);
		}
	}
	return member.buttons.size();
}

bool BMessageBox::prependButton(BButton* button)
{
	if (!button)
		return false;
	if (member.buttons.contain(button))
	{
		int pos = member.buttons.find(button);
		member.buttons.move(pos, 0);
		return true;
	}
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button->asset())
			return false;
	}
	member.buttons.prepend(button);
	member.buttonLayout->insertWidget(1, button);
	member.connect(button, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
	return true;
}
BButton* BMessageBox::prependButton(Button button)
{
	if (!button)
		return 0;
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button)
			return 0;
	}
	if (const char* text = member.buttonText(button))
	{
		BButton* btn = new BButton(text);
		btn->setAsset(button);
		member.buttons.prepend(btn);
		member.buttonLayout->insertWidget(1, btn);
		member.connect(btn, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
		return btn;
	}
	return 0;
}

bool BMessageBox::appendButton(BButton* button)
{
	if (!button)
		return false;
	if (member.buttons.contain(button))
	{
		int pos = member.buttons.find(button);
		member.buttons.move(pos, member.buttons.size());
		return true;
	}
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button->asset())
			return false;
	}
	member.buttons.append(button);
	member.buttonLayout->addWidget(button);
	member.connect(button, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
	return true;
}
BButton* BMessageBox::appendButton(Button button)
{
	if (!button)
		return 0;
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button)
			return 0;
	}
	if (const char* text = member.buttonText(button))
	{
		BButton* btn = new BButton(text);
		btn->setAsset(button);
		member.buttons.append(btn);
		member.buttonLayout->addWidget(btn);
		member.connect(btn, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
		return btn;
	}
	return 0;
}

bool BMessageBox::insertButton(int pos, BButton* button)
{
	if (!button)
		return false;
	if (member.buttons.contain(button))
	{
		int index = member.buttons.find(button);
		member.buttons.move(index, pos);
		return true;
	}
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button->asset())
			return false;
	}
	if (!member.buttons.insert(pos, button))
		return false;
	member.buttonLayout->insertWidget(pos, button);
	member.connect(button, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
	return true;
}
BButton* BMessageBox::insertButton(int pos, Button button)
{
	if (!button)
		return 0;
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == button)
			return 0;
	}
	if (const char* text = member.buttonText(button))
	{
		BButton* btn = new BButton(text);
		btn->setAsset(button);
		member.buttons.insert(pos, btn);
		member.buttonLayout->insertWidget(pos, btn);
		member.connect(btn, Signal_Clicked, &member_BMessageBox::slotButtonClicked);
		return btn;
	}
	return 0;
}

bool BMessageBox::removeButton(Button button)
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		BButton* btn = member.buttons[i];
		if (btn->asset() == button)
		{
			member.disconnect(btn);
			this->removeChild(btn);
			member.buttons.remove(btn);
			return true;
		}
	}
	return false;
}
bool BMessageBox::removeButton(BButton* button)
{
	if (!button)
		return false;
	if (!member.buttons.contain(button))
		return false;
	member.disconnect(button);
	return member.buttons.remove(button);
	return true;
}
void BMessageBox::clearButtons()
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		BButton* button = member.buttons[i];
		member.buttonLayout->remove(button);
	}
	member.buttons.clear();
}

int BMessageBox::buttonCount() const
{
	return member.buttons.size();
}
BButton* BMessageBox::button(int index)
{
	return member.buttons(index);
}
const BButton* BMessageBox::button(int index) const
{
	return member.buttons(index);
}

BButton* BMessageBox::namedButton(const BString& name)
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		BButton* button = member.buttons[i];
		if (button->name() == name)
			return button;
	}
	return 0;
}
const BButton* BMessageBox::namedButton(const BString& name) const
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		const BButton* button = member.buttons[i];
		if (button->name() == name)
			return button;
	}
	return 0;
}

BButton* BMessageBox::buttonOfText(const BString& text)
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		BButton* button = member.buttons[i];
		if (button->text() == text)
			return button;
	}
	return 0;
}
const BButton* BMessageBox::buttonOfText(const BString& text) const
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		const BButton* button = member.buttons[i];
		if (button->text() == text)
			return button;
	}
	return 0;
}

BButton* BMessageBox::buttonOfAsset(const BValue& asset)
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		BButton* button = member.buttons[i];
		if (button->asset() == asset)
			return button;
	}
	return 0;
}
const BButton* BMessageBox::buttonOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		const BButton* button = member.buttons[i];
		if (button->asset() == asset)
			return button;
	}
	return 0;
}

bool BMessageBox::setDefaultButton(Button button)
{
	if (member.defaultButton != button)
	{
		member.defaultButton = button;
		this->fresh();
	}
	return true;
}
bool BMessageBox::setDefaultButton(BButton* button)
{
	if (member.buttons.contain(button))
	{
		member.defaultButton = button->asset();
	}
	return true;
}
BButton* BMessageBox::defaultButton() const
{
	for (int i = 0; i < member.buttons.size(); i++)
	{
		if (member.buttons[i]->asset() == member.defaultButton)
			return member.buttons[i];
	}
	return 0;
}

Button BMessageBox::information(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	this->setTitle(title);
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::warning(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	this->setTitle(title);
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::question(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	this->setTitle(title);
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::critical(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	this->setTitle(title);
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::about(const BString& title, const BString& text, int buttons, Button defaultButton)
{
	this->setTitle(title);
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}

Button BMessageBox::information(const BString& text, int buttons, Button defaultButton)
{
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::warning(const BString& text, int buttons, Button defaultButton)
{
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::question(const BString& text, int buttons, Button defaultButton)
{
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::critical(const BString& text, int buttons, Button defaultButton)
{
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}
Button BMessageBox::about(const BString& text, int buttons, Button defaultButton)
{
	this->setText(text);
	this->setButtons(buttons);
	this->setDefaultButton(defaultButton);
	return this->execute();
}

BSize BMessageBox::sizeHint() const
{
	BDialog::sizeHint();
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	return this->preferSize(member.textSize);
}

void BMessageBox::popupEvent(const BEvent& event)
{
	if (event.value() && member.defaultButton)
	{
		for (int i = 0; i < member.buttons.size(); i++)
		{
			BButton* button = member.buttons[i];
			if (button->asset() == member.defaultButton)
			{
				button->setFocused(true);
				event.accept();
				break;
			}
		}
	}
	BDialog::popupEvent(event);
}

void BMessageBox::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BDialog::resizeEvent(event);
}

void BMessageBox::styleEvent(const BEvent& event)
{
	const BStyle* style = event.value();
	style_member(style)->assign(Value_Align, member.textAlign);
	style_member(style)->assign(Value_Line_Height, member.block.lineSize);
	style_member(style)->assign(Value_Line_Spacing, member.block.lineSpacing);
	member.dirty = true;
	BDialog::styleEvent(event);
}
void BMessageBox::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BDialog::freshEvent(event);
}
void BMessageBox::paintEvent(const BEvent& event)
{
	BDialog::paintEvent(event);

	BPainter painter(this);
	painter.setClip(this->centerRect());

	State state = painter.state();
	if (painter.setColor(Color_Text, state))
	{
		member.block.paint(painter);
	}
}

void BMessageBox::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (member.buttons.size())
	{
		int index = -1;
		for (int i = 0; i < member.buttons.size(); i++)
		{
			if (member.buttons[i]->focused())
			{
				index = i;
				break;
			}
		}
		if (keyEvent.key() == Key_Left || keyEvent.key() == Key_Right)
		{
			if (keyEvent.key() == Key_Left)
				index--;
			if (keyEvent.key() == Key_Right)
				index++;
			if (index < 0)
				index = 0;
			if (index >= member.buttons.size())
				index = member.buttons.size() - 1;
			member.buttons[index]->setFocused(true);
		}
		else if (keyEvent.key() == Key_Enter)
		{
			if (BButton* btn = member.buttons(index))
			{
				Button button = btn->asset();
				this->close(button);
			}
		}
	}
	return BDialog::keyPressEvent(keyEvent);
}
void BMessageBox::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	return BDialog::keyReleaseEvent(keyEvent);
}
